#include <iostream>
#include <stdio.h>
#include <stdlib.h>

#include "../API/GraphDatabase.hpp"
#include "../API/BFPredictor.hpp"
#include "../API/GraphFunctions.hpp"
#include "../API/TestingSet.hpp"
#include "../API/AlgorithmStats.hpp"
#include "../API/NBFPredictor.hpp"
#include "../API/ullman.hpp"
#include <boost/graph/isomorphism.hpp>
#include <boost/graph/iteration_macros.hpp>

#include <fstream>

using namespace std;
using namespace boost;
using namespace TopologicalLearner;


void SubgraphIsoTest(GraphDatabase& D)
{
    const std::vector<FrequentSubgraph>& FSG = D.GetFrequentSubgraphVector();
    for(unsigned int i=0; i < FSG.size(); i++)
    {
        cout << i << endl;

        if( !GraphFunctions::IsSubgraphContained(FSG[i].G, D.GetGraph(FSG[i].vGraphIDs[0]), false ) )
            cout << "Failed" << endl;
    }
}

void SaveFrequentSubgraphs(GraphDatabase& D, string sDir)
{
    stringstream f, run;
    const std::vector<FrequentSubgraph>& FSG = D.GetFrequentSubgraphVector();
    // Save all FSGs 1-100
    for(int i=0; i < min(100,(int) FSG.size()); i++)
    {
        f << sDir << "/" << i << ".dot";
        cout << f.str() << endl;
        GraphFunctions::SaveGraph(f.str().c_str(), FSG[i].G, &D);

        stringstream run;

        run << "dot -Tpng " << f.str() << " -o " << f.str() << ".png";
        cout << run.str() << endl;
        system (run.str().c_str());

        run.str("");
        f.str("");

    }

}

std::pair<EditOp, double> getHighestEditOP(EditOpDist eDist){
    EditOpDist::iterator it;
    EditOp e;
    double highestProb = -1;
    for (it = eDist.begin(); it != eDist.end(); ++it){

        if (it->second > highestProb){
            e = it->first;
            highestProb = it->second;
        }
    }
    return make_pair(e,highestProb);
}


/*
 *
 */
EditOpDist evaluateEditOpDist(EditOpDist& eDist, const Graph& partialGraph, CategoryDegreeDist& degreeList, CategToCategDist& catToCatList, GraphDatabase& D){

    EditOpDist::iterator it;
    EditOpDist newEDist;
    int i = 0;
    stringstream ps;
    for (it = eDist.begin(); it != eDist.end(); ++it){
        Graph g = GraphFunctions::ApplyEditOp(partialGraph, it->first,false);

        /* First check the degree value
            * 1. Get the degree of the edge in the new graph
            * 2. Find the corresponding entry in degreeList
            */

        // for debug:

        /*  EditOp e1 = it->first;
        int olddegree = in_degree(e1.get<1>(), partialGraph);
        double olddegreeProb = degreeList[e1.get<1>()][olddegree];
        cout << "old degree is: " << olddegree << endl;
        cout << "old degree prob is: " << olddegreeProb << endl;*/

        EditOp e = it->first;
        int newdegree = in_degree(e.get<1>(), g);
        //         cout << i << "th " << D.GetStringFromVertexID(get(vertex_name, partialGraph, e.get<1>())) << "(" << get(vertex_name, partialGraph, e.get<1>()) << ") " << degreeList[e.get<1>()].size() << " " << newdegree << endl;
        double degreeProb = degreeList[get(vertex_name, partialGraph, e.get<1>())][newdegree];
        //       cout << "new degree is: " << newdegree << endl;
        //    cout << "degree prob is: " << degreeProb << endl;

        /* Then check the cat-cat distribution value
         * 1. First get the newly added or connected vertex's category, c
         * 2. Then get the number of vertices that are adjacent to this vertex with category c
         * 3. Look the value up from the category-category distribution list
        */

        // Debug

        unsigned int targetCatId;
        if (it->first.get<0>()){
            targetCatId = it->first.get<2>();
        }
        else{
            targetCatId = get(boost::vertex_name, partialGraph, it->first.get<2>());
        }

        /*
        cout << "i: " << i << endl;
        cout << "Add vertex operation? " << it->first.get<0>() << endl;

        cout << "New vertex is: " <<  D.GetStringFromVertexID(targetCatId) << endl;
        cout << "To existing vertex: " <<  D.GetStringFromVertexID(get(boost::vertex_name, partialGraph, it->first.get<1>())) << endl;*/


        boost::graph_traits<Graph>::adjacency_iterator ai2, ai_end2;
        int oldnumberOfAdjacentCategory = 0;
        for (tie(ai2, ai_end2) = adjacent_vertices(it->first.get<1>(), partialGraph);ai2 != ai_end2; ++ai2)
        {
            // cout << "neighbor: " << D.GetStringFromVertexID(get(boost::vertex_name, partialGraph, *ai2)) << endl;

            if (get(boost::vertex_name, partialGraph, *ai2) == targetCatId){
                // cout << " found " << endl;
                oldnumberOfAdjacentCategory++;
            }
        }

        /*  cout << "before oldnumberOfAdjacentCategory: " << oldnumberOfAdjacentCategory << endl;
        cout << endl;*/
        boost::graph_traits<Graph>::adjacency_iterator ai, ai_end;
        int numberOfAdjacentCategory = 0;
        for (tie(ai, ai_end) = adjacent_vertices(it->first.get<1>(), g);ai != ai_end; ++ai)
        {
            //     cout << "neighbor: " << D.GetStringFromVertexID(get(boost::vertex_name, g, *ai)) << endl;


            if (get(boost::vertex_name, g, *ai) == targetCatId){
                //         cout << " found " << endl;
                numberOfAdjacentCategory++;
            }


        }
        double categorydist = 1;

        // MASSIVE BUG: After removing rare vertices, size of catToCatList is 25 but get(...) returns 39 for a category, meaning
        // there's a mismatch between how many categories are there.

        //              cout << get(boost::vertex_name, g, e.get<1>()) << " " << catToCatList.size() << " " << catToCatList[get(boost::vertex_name, g, e.get<1>())].size() << " " << targetCatId << endl;
        vector<double> catcat = catToCatList[get(boost::vertex_name, g, e.get<1>())][targetCatId];
        //         cout << D.GetStringFromVertexID(get(boost::vertex_name, g, e.get<1>())) << " " << catcat.size() << " " << numberOfAdjacentCategory << endl;
        //       cout << __LINE__ << endl;

        categorydist = catToCatList[get(boost::vertex_name, g, e.get<1>())][targetCatId][numberOfAdjacentCategory];

        assert(oldnumberOfAdjacentCategory != numberOfAdjacentCategory);

        /*        cout << "after numberOfAdjacentCategory: " << numberOfAdjacentCategory << endl;
        cout << "number of type with first,second:  " << D.GetStringFromVertexID(it->first.get<1>()) << "," << D.GetStringFromVertexID(it->first.get<2>()) << " " << numberOfAdjacentCategory << endl;
        cout << "category dist: " << categorydist << endl;
        cout << "is isomorphic?: " << boost::isomorphism(partialGraph,g) << endl;
        cout << "-------------------------------------------" << endl;*/
        i++;
        double newprob =  it->second*degreeProb*categorydist;
        //  cout << "newprob is " << newprob << endl;


        newEDist[it->first] = newprob;


    }

    return newEDist;
}


bool lazySubgraphIsomorphism(const Graph& mainGraph, std::map<Vertex, Vertex> graphMapping, EditOp& lastEditOp){

    if (graphMapping.size() == 0){
        cout << "Mapping has zero size! returning false." << endl;
        return false;
    }
    // If this is an edge operation
    if (lastEditOp.get<0>()){

        // Get the mapping
        Vertex v = graphMapping[lastEditOp.get<1>()];
        // Now for all edges of this vertex in the main graph, check if one of them is of edited category
        BGL_FORALL_OUTEDGES(v, e, mainGraph, Graph){

            Vertex targetv = target(e,mainGraph);
            // If the vertex edited in the mainGraph has a neighbor with same category as the one we're adding
            if (lastEditOp.get<2>() == get(vertex_name, mainGraph, targetv)){
                bool isAlreadyExisting = false;
                // Still it might have existing vertices with the same category before we edited, so check that  the neighbor we found
                // is not already in the partial graph.
                for (std::map<Vertex,Vertex>::iterator it = graphMapping.begin(); it != graphMapping.end(); ++it){
                    if( it->second == targetv){

                        isAlreadyExisting = true;
                        break;
                    }
                }
                if (!isAlreadyExisting){
                    return true;
                }
            }
        }

        return false;
    }
    else {
        // Get the mapping
        Vertex target = graphMapping[lastEditOp.get<1>()];
        // Get the mapping
        Vertex source = graphMapping[lastEditOp.get<2>()];
        return boost::edge(target,source,mainGraph).second;
    }
}


int main()
{
    GraphDatabase D;

    int centralNodenessThreshold = 3;
    int minGraphSize = 5;

    // Load graphs from XML
    /*
    D.LoadAllGraphs("corpus/", -1,false);

    D.ReplaceCategory("LAB SV", "RS LAB");
    D.ReplaceCategory("RS LO", "RS LAB");

    D.ReplaceCategory("F LAV", "BATH");
    D.ReplaceCategory("M LAV", "BATH");
    D.ReplaceCategory("P LAV", "BATH");

    D.ReplaceCategory("OFF SV", "OFF");

    D.RemoveIsolatedVertices();
    D.RemoveDisconnectedGraphs();
    D.BlacklistOnCriterion(500);

    D.GenerateCategoryDegreeDist();
    D.GenerateCatgToCatgDist();

    D.RemoveGraphsBasedOnSize(minGraphSize);
    D.MergeCentralNodes(centralNodenessThreshold, D.GetVertexIDFromString("CORR"));
    D.Save("GD_raw.dat");

    return 1;*/

    /*
    D.Load("GD_raw.dat");
    D.GenerateCategoryDegreeDist();

    cout << "Segmenting graph database..." << endl;;
    GraphDatabase segmentedD =
    GraphFunctions::ExtractSegments(D, "segments", D.GetDegreeDist(), centralNodenessThreshold, false, minGraphSize);

    cout << "Removing graphs based on size..." << endl;
    segmentedD.RemoveGraphsBasedOnSize(minGraphSize);
    cout << "Generating frequent subgraphs..." << endl;
    segmentedD.GenerateFrequentSubgraphs(0.1);
    cout << "Generating distributions..." << endl;
    segmentedD.GenerateCategoryDegreeDist();
    segmentedD.GenerateCatgToCatgDist();
    cout << "Done!" << endl;

    segmentedD.Save("GD_segmented.dat");
    return 1;*/

    //GraphDatabase D;
    /*
        D.Load("GD_raw.dat");
    D.GenerateCategoryDegreeDist();

    //GraphDatabase D1;
    //D.LoadSingleGraph("corpus/3-0.xml");

    int iID = D.GetGraphNameFromID("3-0.xml");
    cout << "id: " << iID << endl;

    GraphFunctions::SaveGraphToFile("graph.dot", D.GetGraph(iID), D);

    set<int> freq;
    freq.insert(1);

    vector<Graph> ret = D.SegmentGraph(D.GetGraph(iID),freq, centralNodenessThreshold);
    //vector<Graph> ret = D.SegmentGraph(G0,freq, centralNodenessThreshold);
    GraphFunctions::SaveVectorOfGraphs(D, "testsegments/",ret, 100);

    //GraphDatabase dTest =
    //GraphFunctions::ExtractSegments(D, "testsegments", D.GetDegreeDist(), centralNodenessThreshold, false, minGraphSize);

    //GraphFunctions::SaveVectorOfGraphs(dTest, "testsegments/",dTest.GetGraphVector(), 100);

    return 1;*/

    //GraphDatabase segmentedD;
    //segmentedD.Load("GD_segmented.dat");


    //    GraphFunctions::SaveGraphToFile("graph.dot", segmentedD.GetGraph(1426), segmentedD);

    //return 1;
    vector<Graph> g;
    /*
    for (unsigned int i = 0; i < segmentedD.GetFrequentSubgraphVector().size(); i++){
        g.push_back(segmentedD.GetFrequentSubgraphVector()[i].G);
    }
    GraphFunctions::SaveVectorOfGraphs(segmentedD, "FSG_segments/",g, 100);*/
    /*
    Graph testg;
    Vertex v = add_vertex(testg);
    put(vertex_name, testg, v, segmentedD.GetVertexIDFromString("CORR"));
    Vertex v1 = add_vertex(testg);
    put(vertex_name, testg, v1, segmentedD.GetVertexIDFromString("CORR"));
    add_edge(v,v1,testg);

    GraphFunctions::SaveGraphToFile("testg", testg, segmentedD);
    int included = 0;
    for (unsigned int i = 0; i < segmentedD.GetNumGraphs(); i++){
        cout << "checning graph " << i  << " of " << segmentedD.GetNumGraphs() << endl;
        if (GraphFunctions::IsSubgraphContained(testg, segmentedD.GetGraphVector()[i],false)){
            included++;
            GraphFunctions::SaveGraphToFile(segmentedD.GetGraphFilenameVector()[i] + "weird.dot",segmentedD.GetGraphVector()[i], segmentedD);
            cout << "in " << segmentedD.GetGraphFilenameVector()[i] << endl;
        }
    }

    cout << "fjaf" << included << endl;

    return 1;

    return 1;*/

    D.Load("GD_segmented.dat");
    /*
    D.GenerateFrequentSubgraphs(0.08);
    cout << "FSG size: " << D.GetFrequentSubgraphVector().size() << endl;
    D.Save("GD_segmented.dat");
    return 1;*/

    D.GenerateCategoryDegreeDist();
    D.GenerateCatgToCatgDist();
    D.ExportAveDegreesAvgCluster("avedegree");
    CategoryDegreeDist dDist = D.GetDegreeDist();
    CategToCategDist kCatg = D.GetCatgToCatgDist();
    CategoryDegreeDist::iterator it = dDist.begin();
    for (it; it != dDist.end(); ++it)
        cout << D.m_kLoader.m_IDTypes[it->first] << " " << it->second.size() << endl;


    NBFPredictor PD;
    PD.AttachDatabase(&D);


    TestingSet kTest;

    // We want to generate graphs of graph size 1-10, and 100 of each
    // thus in total 1000 different graphs.
    vector<unsigned int> sizes;
    sizes.push_back(0);


    sizes.push_back(300);
    //sizes.push_back(3);
    //sizes.push_back(3);

    kTest.GenerateTestingSet(sizes, D);
    kTest.Save("testingset.dat");
    //kTest.Load("testingset.dat");

    //    kTest.Load("testingset.dat");
    /*
    // Perform the benchmark
    AlgorithmStats stats;
    double dResult = stats.TestAll(kTest, &PD, "BFPredictor.dat");
    cout << dResult << endl;
*/
    // return 1;

    int k = 0;
    int NBFcorrectPred = 0;
    int NBFincorrectPred = 0;

    int ENBFcorrectPred = 0;
    int ENBFincorrectPred = 0;

    std::vector<std::pair<size_t,size_t> > mapping;
    std::vector<double> E,C;
    int binlength = 10;
    for (unsigned int i =0; i < binlength; i++){
        E.push_back(0);
        C.push_back(0);
    }

    trueEdgePredicate blabel;
    for(unsigned int i=0; i < kTest.m_PGSizes.size(); i++)
    {
        cout << "Testing graphs of size: " << i << endl;
        for(unsigned int j=0; j < kTest.m_PGSizes[i]; j++)
        {
            VertexLabelPredicate test (kTest.m_PGs[k].second,D.GetGraph(kTest.m_PGs[k].first));
            if(!ullmann(kTest.m_PGs[k].second, D.GetGraph(kTest.m_PGs[k].first),test, blabel,mapping))
                abort();

            PD.SetExcludeGraph(kTest.m_PGs[k].first);
            //   cout << __LINE__ << endl;

            // First the NBF method
            //   cout << k << " " << D.GetGraphFilenameVector()[kTest.m_PGs[k].first] <<  endl;
            std::string filename = D.GetGraphFilenameVector()[kTest.m_PGs[k].first];
            GraphFunctions::SaveGraphToFile(QString("testset/test%1").arg(k).toStdString() + filename,kTest.m_PGs[k].second, D);
            GraphFunctions::SaveGraphToFile(QString("testset/main%1").arg(k).toStdString() + filename,D.GetGraph(kTest.m_PGs[k].first), D);

            cout << "Predicting.." << endl;
            EditOpDist kResDist = PD.Predict(kTest.m_PGs[k].second);
            cout << kResDist.size() << endl;
            if (kResDist.size() != 0){
                //kTest.Save("testingset.dat");


                for (EditOpDist::iterator it = kResDist.begin(); it != kResDist.end(); ++it){
                    cout << "Probability: " << it->second << endl;
                    Graph g = kTest.m_PGs[k].second;
                    EditOp e = it->first;
                    GraphFunctions::PrintEditOp(g, e, D);
                    cout << "------------------------------" << endl;
                }
                EditOp NBFbestEditOP;
                pair<EditOp,double> bestPair = getHighestEditOP(kResDist);
                NBFbestEditOP = bestPair.first;

                GraphFunctions::PrintEditOp(kTest.m_PGs[k].second, NBFbestEditOP, D);
                cout<< "---------" << bestPair.second << "-------" << endl;


                Graph NBFPredictedGraph = GraphFunctions::ApplyEditOp(kTest.m_PGs[k].second,NBFbestEditOP,false);

                VertexLabelPredicate NBFalabel(NBFPredictedGraph,D.GetGraph(kTest.m_PGs[k].first));
                // Check if those exist in the big graph
                cout << "Checking if the best prediction is correct." << endl;
                if (ullmann(NBFPredictedGraph, D.GetGraph(kTest.m_PGs[k].first),NBFalabel, blabel,mapping))
                {
                    NBFcorrectPred++;
                }
                else{
                    NBFincorrectPred++;
                    GraphFunctions::SaveOverlayGraphToFile(QString("falsepredictions/wrong%1").arg(k).toStdString() + "_" + filename, D.GetGraph(kTest.m_PGs[k].first), kTest.m_PGs[k].second, NBFPredictedGraph, D);
                    GraphFunctions::SaveGraphToFile(QString("falsepredictions/enbf%1").arg(k).toStdString() + "_" + filename, NBFPredictedGraph, D);
                }

                EditOpDist::iterator it;
                EditOp e;
                vector<bool> isAdded(binlength,false);

                for (it = kResDist.begin(); it != kResDist.end(); ++it){
                    int i = floor(it->second*binlength);
                    E[i] += 1;
                    Graph tmpGraph = GraphFunctions::ApplyEditOp(kTest.m_PGs[k].second,it->first,false);
                //    GraphFunctions::SaveGraphToFile(QString("changed%1").arg(it->second).toStdString() + "_" + filename, tmpGraph, D);

                    VertexLabelPredicate tmlabel(tmpGraph,D.GetGraph(kTest.m_PGs[k].first));
                    if (ullmann(tmpGraph, D.GetGraph(kTest.m_PGs[k].first),tmlabel, blabel,mapping))
                    {
                        cout << "lalala" << endl;
                        C[i]+= 1;
                    }
                }

            }
            else
                cout << "zero distribution!" << endl;

            cout << "total k: " << k << endl;
            cout << "total NBF correctPred/incorrect: " << NBFcorrectPred << " , " << NBFincorrectPred << endl;
            if ( (NBFcorrectPred + NBFincorrectPred) != 0 )
                cout << " , " << 100* NBFcorrectPred / (NBFcorrectPred + NBFincorrectPred) << "%" << endl;
            cout << k << "th partial graph tested." << endl;
            k++;
        }

    }

    std::ofstream out("e.txt");
    for (unsigned int i=0; i < E.size(); i++){
        out << E[i] << "," << C[i] << endl;
    }
    out.close();

    return 1;

    /*
    D.GenerateCategoryDegreeDist();
    CategoryDegreeDist dDist = D.GetDegreeDist();
    D.GenerateCatgToCatgDist();
    CategToCategDist kCatg = D.GetCatgToCatgDist();

    const std::vector<FrequentSubgraph>& FSG = D.GetFrequentSubgraphVector();
    Graph G = FSG[99].G;

    stringstream ps, run;
    ps << "initial" << ".dot";
    GraphFunctions::SaveGraph(ps.str().c_str(), G, &D);
    run << "dot -Tpng " << ps.str() << " -o " << ps.str() << ".png";
    cout << run.str() << endl;
    system (run.str().c_str());
    ps.str("");
    run.str("");

    //std::pair<bool, std::vector< std::map<Vertex, Vertex> > > kres;
    //kres = GraphFunctions::IsSubgraphContained(G, G, false, true);
    //cout << "kres.second(): " << kres.second.size() << endl;
    //return 1;
    NBFPredictor PD;
    PD.AttachDatabase(&D);

    for(int i = 0; i < 30; i++)
    {
        cout << "------------------------------" << endl; cout.flush();

        EditOpDist kResDist = PD.Predict(G);
        EditOpDist knewResDist = evaluateEditOpDist(kResDist,G,dDist,kCatg,D);
        EditOp bestEditOP = getHighestEditOP(knewResDist);
        GraphFunctions::PrintEditOp(G, bestEditOP, D);
        Graph g = GraphFunctions::ApplyEditOp(G,bestEditOP,false);

        ps << "predicted" << i << ".dot";
        GraphFunctions::SaveGraph(ps.str().c_str(), g, &D);

        run << "dot -Tpng " << ps.str() << " -o " << ps.str() << ".png";
        cout << run.str() << endl;
        system (run.str().c_str());

        ps.str("");
        run.str("");

        G = g;
    }*/



    /*
  GraphFunctions gf;
   cout << "Edge operation? : " << !it->first.get<0>() << endl;
   cout << "get<2>:" << it->first.get<2>() << endl;
   cout << "new vertex category:" << D.GetStringFromVertexID(it->first.get<2>()) << endl;
   cout << "existing vertex id:" << it->first.get<1>() << endl;
*/
    // GraphFunctions::SaveGraph("initialgraph.dot", C[0], &D);
    // GraphFunctions::SaveGraph("predictedgraph.dot", g, &D);

    //GraphFunctions::IsSubgraphContained(FSG[1589].G, D.GetGraph(FSG[1589].vGraphIDs[0]), false );


    //D.Save("GD.dat");

    // SubgraphIsoTest(D);

    // Graph G1 = D.GetGraph(100);


    // Get graph # 10 and save it to file (dot-format)
    //  GraphFunctions::SaveGraph("graph.dot", G1, &D);





    //return 1;

    Graph G1;

    // Apply the edit operation add vertex, add an office
    int iOfficeID = D.GetVertexIDFromString("OFF");
    G1 = GraphFunctions::ApplyEditOp(G1, EditOp(true, 0, iOfficeID));

    // Add a corridor and connect it to the office
    int iCorrID = D.GetVertexIDFromString("CORR");
    G1 = GraphFunctions::ApplyEditOp(G1, EditOp(true, iOfficeID, iCorrID));

    GraphFunctions::SaveGraph("graph.dot", G1, &D);

    NBFPredictor BFP;
    BFP.AttachDatabase(&D);

    /*
    EditOpDist kResDist = BFP.Predict(G1);

    EditOpDist::iterator it;
    for(it = kResDist.begin(); it != kResDist.end(); it++ )
    {
        cout << it->second << endl;
    }
*/
    /*  TestingSet kTest;

    // We want to generate graphs of graph size 1-10, and 100 of each
    // thus in total 1000 different graphs.
    vector<unsigned int> sizes;
    sizes.push_back(0);
    for(int i=1; i <10; i++)
        sizes.push_back(100);

    kTest.GenerateTestingSet(sizes, D);

    BFPredictor P;
    P.AttachDatabase(&D);

    NBFPredictor P1;
    P1.AttachDatabase(&D);

    double dResult = 0.0;
    cout << "BF Predictor:" << endl;
    // Perform the benchmark
    AlgorithmStats stats;
    dResult = stats.TestAll(kTest, &P, "BFPredictor.dat");
    cout << dResult << endl;

    cout << "NBF Predictor:" << endl;
    dResult = stats.TestAll(kTest, &P1, "NBFPredictor.dat");
    cout << dResult << endl;

    return 0;*/


    // D.Load("GD.dat");
    //int iCorrID1 = D.GetVertexIDFromString("CORR");
    // Save loaded graphs from XML

    //
    // SaveFrequentSubgraphs(D, "FSGs/");

    /*




    for( int i=0; i < dDist[iCorrID1].size(); i++)
    {
        cout << "i: " << i << ", d:" << dDist[iCorrID1][i] << endl;
    }

    GraphFunctions::SaveGraph("graph.dot", D.GetGraph(0), &D);
   // GraphFunctions::SaveGraph("graph2.dot", D.GetGraph(157), &D);*/

    //D.ExportCategoryDist(iCorrID1, "CorrDegreeDist.dat");
    //D.ExportCategoryDist(D.GetVertexIDFromString("OFF"), "OffDegreeDist.dat");
    // D.ExportCategoryDist(D.GetVertexIDFromString("U/M"), "UMDegreeDist.dat");
    /* D.GenerateCatgToCatgDist();
    GraphFunctions::SaveGraph("graph.dot", D.GetGraph(1), &D);

    vector<double> dTmpV = kCatg[iCorrID1][D.GetVertexIDFromString("OFF")];
    for(int i=0; i < dTmpV.size(); i++)
        cout << "i: " << i << ", P: " << dTmpV[i] << endl;

            cout << "iOffID: " << D.GetVertexIDFromString("OFF") << endl;
    return 1;*/
    // 6152 with unique



    //  D.LoadFrequentSubgraphs("tmp.fp");
    //
    //  cout << "Frequent subgraphs mined: " << FSG.size() << endl;

    //cout << FSG[1].vGraphIDs[0] << endl;r
    //  return 1;


    //cout << FSG[1999].vGraphIDs.size() << endl;
    //cout << FSG[2000].vGraphIDs.size() << endl;
    // ID99'
    //    GraphFunctions::SaveGraph("graph.dot", FSG[99].G, &D);
    //   GraphFunctions::SaveGraph("graph2.dot", FSG[5].G, &D);


    //std::set< std::pair< const FrequentSubgraph*, VertexMappings > > kRes;
    //kRes = D.GetOccuringFrequentSubgraphs(FSG[99].G, 1);

    //EditOpDist GraphFunctions::GetDistribution(const std::vector<Graph>& C,
    //  const GraphDatabase& D, int iIndex, const Graph& G, int iExcludeGraph, bool bUnique)


    /*

    //EditOpDist::iterator it;
    //for(it = kResDist.begin(); it != kResDist.end(); it++ )
    //    cout << it->second << endl;
*/
}
